﻿using Autofac.Core;

using Newtonsoft.Json;

using NPOI.SS.Formula.Functions;

using PLCS.Application.Contracts.EquipmentApp.Dtos;
using PLCS.Domain.Equipments;
using PLCS.Domain.Shared.Tasks;
using PLCS.Domain.Stations;
using PLCS.Domain.Tasks;
using PLCS.Shared.Common.Constant;
using PLCS.SocialHttpClient.WMS.Models;

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

using Volo.Abp.DependencyInjection;

namespace PLCS.EquipmentService.ParsersHelpers;

/// <summary>
/// 任务解析类
/// </summary>
public class PathAnalyzer : ITransientDependency
{
    private readonly IStationRepository _stationRepository;
    private readonly ILogger<PathAnalyzer> _logger;
    private readonly IPathResultRepository _pathResultRepository;
    private readonly TaskManager _taskManager;
    private readonly ITaskRepository _taskRepository;
    private readonly IEquipmentRepository _equipmentRepository;
    private readonly IInstructionRepository _instructionRepository;
    private readonly IRepository<InstructionTimeLog, Guid> _instructionTimeLogrepository;

    public PathAnalyzer(
        IStationRepository stationRepository,
        ILogger<PathAnalyzer> logger,
        IPathResultRepository pathResultRepository,
        TaskManager taskManager,
        ITaskRepository taskRepository,
        IEquipmentRepository equipmentRepository,
        IInstructionRepository instructionRepository,
        IRepository<InstructionTimeLog, Guid> instructionTimeLogrepository)
    {
        _stationRepository = stationRepository;
        _logger = logger;
        _pathResultRepository = pathResultRepository;
        _taskManager = taskManager;
        _taskRepository = taskRepository;
        _equipmentRepository = equipmentRepository;
        _instructionRepository = instructionRepository;
        _instructionTimeLogrepository = instructionTimeLogrepository;
    }

    /// <summary>
    /// 拆分出最优的任务路线
    /// </summary>
    /// <param name="start">大的起始位</param>
    /// <param name="end">大的目标位</param>
    /// <returns></returns>
    public async Task<List<Instruction>> SplitOptimalPathTasksAsync(TaskModel task)
    {
        var start = task.StartPos;
        var end = task.EndPos;
        var wayStation = task.WayStation;
        var instructions = new List<Instruction>();

        if (string.IsNullOrEmpty(start) || string.IsNullOrEmpty(end))
            return instructions;

        // 获取所有站点信息
        var stations = await _stationRepository.GetStationListAsync();
        var equipments = await _equipmentRepository.GetListAsync();
        var optimalPath = await SplitBestPathInWorkstationsAboutWayStationAsync(start, end, wayStation, stations);
        if (optimalPath == null)
            return instructions;

        instructions = await GenerateInstructionsFromPathAsync(optimalPath, task, stations);

        // 返回指令列表
        return instructions;
    }

    public async Task<List<Instruction>> SplitOptimalPathTasksAsync(string start, string end)
    {
        var instructions = new List<Instruction>();

        // 获取所有站点信息
        var stations = await _stationRepository.GetStationListAsync();

        var optimalPath = await SplitBestPathInWorkstationsAboutWayStationAsync(start, end, null, stations);
        if (optimalPath == null)
            return instructions;

        instructions = await GenerateInstructionsFromPathAsync(optimalPath, null, stations);

        // 返回任务列表
        return instructions;
    }

    /// <summary>
    /// 拆分出最优任务路线子方法（途径点版）
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="wayStation"></param>
    /// <param name="stations"></param>
    /// <returns></returns>
    private async Task<List<string>> SplitBestPathInWorkstationsAboutWayStationAsync(string start, string end, string? wayStation = null, IEnumerable<Station>? stations = null)
    {
        stations ??= await _stationRepository.GetStationListAsync();
        var wayList = new List<string>();
        wayList.Add(start);
        if (!string.IsNullOrEmpty(wayStation))
        {
            var wayStations = wayStation.Split(ConstantChar.SplitChar);
            wayList.AddRange(wayStations);
        }
        wayList.Add(end);
        var pathList = new List<string>();
        for (int i = 0; i < wayList.Count; i++)
        {
            if (i != wayList.Count - 1)
            {
                var list = await SplitBestPathInWorkstationsAsync(wayList[i], wayList[i + 1], stations);
                if (list.Count == 0)
                    return null;
                pathList.Remove(pathList.LastOrDefault());
                pathList.AddRange(list);
            }
        }
        return pathList;
    }

    /// <summary>
    /// 拆分出最优任务路线子方法
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="stations"></param>
    /// <returns></returns>
    private async Task<List<string>> SplitBestPathInWorkstationsAsync(string start, string end, IEnumerable<Station> stations)
    {
        // 用于存储任务信息
        var instructions = new List<string>();
        // 将所有站点编号存储到 HashSet 中，提高查找效率
        HashSet<string> stationSet = new(stations.Select(x => x.StaNum));
        // 判断起始工位和目标工位是否存在
        if (!stationSet.Contains(start) || !stationSet.Contains(end))
            return instructions;
        // 获取所有路径信息
        var paths = await FindAllPathsAsync(start, end, stations);

        // 如果没有找到路径，则直接返回空任务列表
        if (!paths.Any())
        {
            return instructions;
        }

        // 获取最优路径
        var optimalPath = await ChooseBestPathInWorkstationsAsync(paths, stations);
        return optimalPath;
    }

    /// <summary>
    /// 从多个路径中选择最佳路径。
    /// </summary>
    /// <param name="paths">包含多条路径的列表。</param>
    /// <param name="stations">包含所有工位的列表。</param>
    /// <returns>符合条件的路径或 null 如果未找到符合条件的路径。</returns>
    private async Task<List<string>> ChooseBestPathInWorkstationsAsync(List<List<string>> paths, IEnumerable<Station> stations)
    {
        // 先过滤掉有故障且暂停使用的工位
        stations = stations.Where(x => x.IsUse == YesNo.是 && x.IsLocked == YesNo.否);
        //var waitComparePaths = paths.OrderBy(x => x.Count).Take(5);

        int minCount = paths.Min(x => x.Count);
        var sameLengthPaths = paths.Where(x => x.Count == minCount).ToList();
        int firstTurningPointIndex = FindFirstDiffNode(sameLengthPaths);
        var tasks = await _instructionRepository.GetUnfinishedInstructionsAsync();

        List<List<string>> canChosePaths = new(sameLengthPaths);
        if (firstTurningPointIndex != -1)
        {
            canChosePaths = new List<List<string>>();
            // 先遍历第一遍，看存不存在哪一个转折点工位是既是空位，且没有任务来的，有的话直接返回
            foreach (var item in sameLengthPaths)
            {
                var turningStation = item[firstTurningPointIndex];
                var station = stations.SingleOrDefault(x => x.StaNum == turningStation);
                var hasActiveTask = tasks.Any(x => x.EndPos == turningStation);
                if (station != null && station.HaveStatus == YesNo.否 && !hasActiveTask)
                    canChosePaths.Add(item);
            }

            if (!canChosePaths.Any())
            {
                //如果第一个遍历没有找到合适的工位，那就优先选择满位且没有任务来的，因为有任务来的肯定比已经满位的要慢
                foreach (var item in sameLengthPaths)
                {
                    var turningStation = item[firstTurningPointIndex];
                    var station = stations.SingleOrDefault(x => x.StaNum == turningStation);
                    var hasActiveTask = tasks.Any(x => x.EndPos == turningStation);
                    if (station != null && station.HaveStatus == YesNo.是 && !hasActiveTask)
                        canChosePaths.Add(item);
                }
            }
        }

        return canChosePaths.First();
    }

    /// <summary>
    /// 找到所有路径(广度优先算法)
    /// </summary>
    /// <param name="startStation">起始站点编号</param>
    /// <param name="endStation">目标站点编号</param>
    /// <param name="stations">所有站点</param>
    /// <returns>所有路径</returns>
    private async Task<List<List<string>>> FindAllPathsAsync(string startStation, string endStation, IEnumerable<Station> stations)
    {
        stations = stations.Where(x => (x.StationType != Domain.Shared.Stations.StationType.无光电货位 || x.StaNum == endStation || x.StaNum == startStation));

        // 初始化队列和已访问站点集合
        var pathQueue = new Queue<List<string>>(); // 队列用于存放路径
        var visitedStations = new HashSet<string>(); // 已访问的站点
        var paths = new List<List<string>>(); // 所有路径
        var whitelistStations = new HashSet<string>();

        visitedStations.Add(startStation); // 将起始站点加入已访问站点集合
        pathQueue.Enqueue(new List<string>() { startStation }); // 将起始站点加入队列

        // 找到起始站点，并遍历起始站点的可取设备，然后遍历所有工位，找到可以送达设备的工位，并将起始站点和当前站点加入队列。

        // 找到起始站点
        var start = stations.FirstOrDefault(x => x.StaNum == startStation);
        //var tolEnd = stations.FirstOrDefault(x => x.StaNum == endStation);
        if (start == null || string.IsNullOrEmpty(start.CanTake_EquNum))
        {
            return paths;
        }

        // 判断能不能起始位能不能直接到达目标位，可以的话直接返回。
        if (IsDirectPathPossible(stations.Single(x => x.StaNum == endStation), start.CanTake_EquNum))
        {
            paths.Add(new List<string>() { startStation, endStation });
            return paths;
        }

        var cachePath = await _pathResultRepository.FindPaths(startStation, endStation);
        if (!string.IsNullOrEmpty(cachePath))
        {
            return DeserializePathDetails(cachePath);
        }

        // 遍历所有起始站点的可取设备
        foreach (var device in start.CanTake_EquNum.Split(ConstantChar.SplitChar))
        {
            var validStations = stations.Where(x => !string.IsNullOrWhiteSpace(x.CanSend_EquNum)
                                                                  && x.StaNum != startStation
                                                                  && x.CanSend_EquNum.Split(ConstantChar.SplitChar).Any(x => x == device));

            // 遍历所有工位，找到可送达设备
            foreach (var station in validStations)
            {
                // 将起始站点和当前站点加入队列
                var newPath = new List<string>() { startStation, station.StaNum };
                pathQueue.Enqueue(newPath);
            }
        }

        //处理起始站点，从队列中取出第一个路径，并将其从队列中移除。然后判断当前站点是否是结束站点，如果是则将路径加入路径列表。
        //否则，遍历当前站点的可取设备，再遍历所有工位，找到可以送达设备的工位，并将路径复制一份，然后加入当前站点，再将路径加入队列。

        // 处理起始站点
        while (pathQueue.Count > 0)
        {
            //从队列中取出第一个路径，并将其从队列中移除。
            var path = pathQueue.Dequeue();
            var lastStation = path.Last();
            if (lastStation == endStation)
            {
                // 如果当前站点是结束站点，将路径加入路径列表
                paths.Add(path);
                continue;
            }

            if (paths.Any() && IsPathContained(paths, path))
            {
                continue;
            }

            var last = stations.FirstOrDefault(x => x.StaNum == lastStation);

            if (last == null || string.IsNullOrEmpty(last.CanTake_EquNum))
            {
                continue;
            }
            foreach (var device in last.CanTake_EquNum.Split(ConstantChar.SplitChar))
            {
                var validStations = stations.Where(x => !string.IsNullOrWhiteSpace(x.CanSend_EquNum)
                                                                    && x.StaNum != lastStation
                                                                    && (!visitedStations.Contains(x.StaNum) || whitelistStations.Contains(x.StaNum))
                                                                    && x.CanSend_EquNum.Split(ConstantChar.SplitChar).Any(x => x == device));
                foreach (var station in validStations)
                {
                    // 将路径复制一份，然后加入当前站点，再将路径加入队列
                    var newPath = new List<string>(path) { station.StaNum };

                    pathQueue.Enqueue(newPath);

                    if (station.StaNum != endStation)
                    {
                        visitedStations.Add(station.StaNum);
                    }
                    else
                    {
                        // 如果是到达终点了，把能到达终点前一个的工位全部从已访问里释放出来
                        var waitDisposeStations = stations.Where(x =>
                              !path.Contains(x.StaNum) && !string.IsNullOrEmpty(x.CanTake_EquNum) && x.CanTake_EquNum.Split(ConstantChar.SplitChar).Any(
                                  y => station.CanSend_EquNum.Split(ConstantChar.SplitChar).Contains(y)
                              )).Select(x => x.StaNum);

                        foreach (var item in waitDisposeStations)
                        {
                            whitelistStations.Add(item);
                        }
                    }
                }
            }
        }

        // 得去重
        var result = paths.Distinct(new ArrayEqualityComparer<string>()).ToList();
        var resultList = new List<List<string>>(result); // 创建一个新的集合来保存移除元素后的结果

        foreach (var item in resultList)
        {
            var remainingItems = resultList.Where(x => !EqualityComparer<List<string>>.Default.Equals(x, item)).ToList();

            if (IsPathContained(remainingItems, item))
            {
                result.Remove(item);
            }
        }

        await _pathResultRepository.CreatePaths(startStation, endStation, SerializePathDetails(result));

        return result;
    }

    #region 工具方法

    private bool IsPathContained(List<List<string>> paths, List<string> path)
    {
        foreach (var item in paths)
        {
            // 将探索好的路径减去目标位
            var existingPath = new List<string>(item);
            existingPath.RemoveAt(existingPath.Count - 1);
            // 判断当前路径是否跟已探索好的路径重复
            if (path.Count >= existingPath.Count)
            {
                bool isMatch = true;
                for (int j = 0; j < existingPath.Count; j++)
                {
                    if (path[j] != existingPath[j])
                    {
                        isMatch = false;
                        break;
                    }
                }
                if (isMatch)
                    return true;
            }
        }
        return false;
    }

    private async Task<List<Instruction>> GenerateInstructionsFromPathAsync(List<string> path, TaskModel? task = null, IEnumerable<Station>? stations = null)
    {
        var instructions = new List<Instruction>();
        stations ??= await _stationRepository.GetStationListAsync();

        int taskId = task == null ? 0 : task.TaskId;
        Guid? taskIdKey = task == null ? null : task.Id;
        // 根据最优路径生成任务列表
        for (int i = 0; i < path.Count - 1; i++)
        {
            // 获取起始站点和结束站点
            var startPos = path[i];
            var endPos = path[i + 1];
            // 获取起始站点和结束站点的信息
            var station = stations.FirstOrDefault(x => x.StaNum == startPos);
            var thenStation = stations.FirstOrDefault(x => x.StaNum == endPos);
            // 获取可以用于取货的设备编号,这边是用join拼接多个设备号了，其实只会有一个（大多数情况只会有一个）
            var equipmentNo = string.Join(ConstantChar.SplitChar, station.CanTake_EquNum.Split(ConstantChar.SplitChar).Where(x => thenStation.CanSend_EquNum.Contains(x)));
            // 生成任务信息
            instructions.Add(new Instruction(GetInstructionNo(taskId, i + 1), taskIdKey, equipmentNo, InstructionType.取放货, path[i], path[i + 1], task.TrayType));
        }
        return instructions;
    }

    /// <summary>
    /// 判断是否存在直接路径（起始位直接到目的位）
    /// </summary>
    /// <param name="endStation"></param>
    /// <param name="startCanTakeEquipment"></param>
    /// <returns></returns>
    private bool IsDirectPathPossible(Station endStation, string startCanTakeEquipment)
    {
        if (string.IsNullOrEmpty(endStation.CanSend_EquNum))
            return false;

        return startCanTakeEquipment.Split(ConstantChar.SplitChar).Any(item => endStation.CanSend_EquNum.Split(ConstantChar.SplitChar).Any(x => x == item));
    }

    /// <summary>
    /// 将路径序列化成Json
    /// </summary>
    /// <param name="pathDetails"></param>
    /// <returns></returns>
    public string SerializePathDetails(List<List<string>> pathDetails)
    {
        return JsonConvert.SerializeObject(pathDetails);
    }

    /// <summary>
    /// 将Json反序列化成路径
    /// </summary>
    /// <param name="serializedPathDetails"></param>
    /// <returns></returns>
    public List<List<string>> DeserializePathDetails(string serializedPathDetails)
    {
        return JsonConvert.DeserializeObject<List<List<string>>>(serializedPathDetails);
    }

    /// <summary>
    /// 拼接一个指令号
    /// </summary>
    /// <param name="taskId"></param>
    /// <param name="index"></param>
    /// <returns></returns>
    private int GetInstructionNo(int taskId, int index)
    {
        // 将第二个数字补全到 9 位数（前面加 0）
        string bStr = index.ToString().PadLeft(9 - taskId.ToString().Length, '0');

        // 拼接成字符串并补全到 9 位数（后面加 0）
        string s = $"{taskId}{bStr.PadRight(9 - taskId.ToString().Length, '0')}";

        // 将新的字符串转换为 int 类型
        return int.Parse(s);
    }

    /// <summary>
    /// 找到多个路线当中的岔路口（就是从哪开始不一样的）
    /// </summary>
    /// <param name="lists"></param>
    /// <returns></returns>
    private int FindFirstDiffNode(List<List<String>> lists)
    {
        int n = lists.Count;
        if (n <= 1)
        {
            return -1;  // 如果只有一个或零个列表，则不存在差异。
        }

        for (int i = 0; i < lists[0].Count; i++)  // 从列表的第一个元素开始进行比较
        {
            for (int j = 1; j < n; j++)
            {
                if (lists[j][i] != lists[0][i])
                {
                    return i;  // 如果发现某一位置不同，则返回该位置的索引
                }
            }
        }

        return -1;  // 如果所有列表都完全相同，则返回-1
    }

    public class ArrayEqualityComparer<T> : IEqualityComparer<List<T>>
    {
        public bool Equals(List<T> x, List<T> y)
        {
            if (x == null && y == null)
                return true;
            if (x == null || y == null)
                return false;

            return x.SequenceEqual(y);
        }

        public int GetHashCode(List<T> obj)
        {
            if (obj == null)
                return 0;

            unchecked
            {
                int hash = 17;
                foreach (T item in obj)
                {
                    hash = hash * 23 + (item?.GetHashCode() ?? 0);
                }
                return hash;
            }
        }
    }

    #endregion 工具方法

    public List<InstructionTimeLog> GenerateAllPossibleTasks(List<Station> stations, List<Equipment> equipments)
    {
        var logs = new List<InstructionTimeLog>();
        foreach (var station in stations)
        {
            if (string.IsNullOrEmpty(station.CanTake_EquNum))
                continue;
            var canTakeEquipments = station.CanTake_EquNum.Split(ConstantChar.SplitChar);
            foreach (var item in canTakeEquipments)
            {
                var canGoStations = stations.Where(x => !string.IsNullOrEmpty(x.CanSend_EquNum) && x.CanSend_EquNum.Contains(item) && x != station);
                foreach (var canGoStation in canGoStations)
                {
                    //var starts = logs.Select(x => x.StartPos);
                    //var ends = logs.Select(x => x.EndPos);
                    //if ((starts.Contains(station.StaNum) && ends.Contains(canGoStation.StaNum)) || (ends.Contains(station.StaNum) && starts.Contains(canGoStation.StaNum)))
                    //    continue;

                    logs.Add(new InstructionTimeLog(station.StaNum, canGoStation.StaNum, 10));
                }
            }
        }

        return logs;
    }
}